﻿namespace Frameworks
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using UnityEngine;
    using UnityEngine.Networking;

    public static class UnityWebRequestUtility
    {
        private static MonoBehaviour _mono;

        #region public Method
        public static void Initialize(MonoBehaviour mono)
        {
            _mono = mono;
        }

        /// <summary>
        /// 下载文本
        /// </summary>
        /// <param name="url"></param>
        /// <param name="action"></param>
        public static void DownloadText(string url, RequestTextEvent action)
        {
            _mono.StartCoroutine(_DownloadText(url, action));
        }

        /// <summary>
        /// 下载数据
        /// </summary>
        /// <param name="url"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static void DownloadData(string url, RequestDataEvent action)
        {
            _mono.StartCoroutine(_DownloadData(url, action));
        }

        /// <summary>
        /// 下载文件[文件存放位置不能重复]
        /// </summary>
        /// <param name="url"></param>
        /// <param name="savePath"></param>
        /// <param name="action"></param>
        public static void DownloadFile(string url, string savePath, RequestFileEvent action)
        {
            _mono.StartCoroutine(_DownloadFile(url, savePath, action));
        }

        /// <summary>
        /// 下载图片
        /// </summary>
        /// <param name="url"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static void DownaloadSprite(string url, RequestSpriteEvent action)
        {
            _mono.StartCoroutine(_DownloadSprite(url, action));
        }

        /// <summary>
        /// 下载音频
        /// </summary>
        /// <param name="url"></param>
        /// <param name="action"></param>
        /// <param name="audioType"></param>
        public static void DownloadAudioClip(string url, RequestAudioClipEvent action, AudioType audioType = AudioType.AUDIOQUEUE)
        {
            _mono.StartCoroutine(_DownloadAudioClip(url, audioType, action));
        }

        /// <summary>
        /// 下载视频
        /// </summary>
        /// <param name="url"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static void DownloadMovieTexture(string url, RequestMovieTextureEvent action)
        {
            _mono.StartCoroutine(_DownloadMovieTexture(url, action));
        }

        /// <summary>
        /// 下载AB包
        /// </summary>
        /// <param name="url"></param>
        /// <param name="action"></param>
        /// <param name="method"></param>
        public static void DownaloadAssetBundle(string url, RequestAssetBundleEvent action)
        {
            _mono.StartCoroutine(_DownloadAssetBundle(url, action));
        }

        /// <summary>
        /// GET请求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="action"></param>
        public static void Get(string url, RequestGenericEvent action)
        {
            _mono.StartCoroutine(_Get(url, action));
        }

        /// <summary>
        /// POST请求
        /// </summary>
        /// <param name="url">服务器请求目标地址</param>
        /// <param name="lstformData">form表单参数</param>
        /// <param name="action"></param>
        public static void Post(string url, List<IMultipartFormSection> lstformData, RequestGenericEvent action)
        {
            //List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
            //formData.Add(new MultipartFormDataSection("field1=foo&field2=bar"));
            //formData.Add(new MultipartFormFileSection("my file data", "myfile.txt"));

            _mono.StartCoroutine(_Post(url, lstformData, action));
        }

        /// <summary>
        /// 通过PUT方式将字节流传到服务器
        /// </summary>
        /// <param name="url"></param>
        /// <param name="contentBytes">需要上传的字节流</param>
        /// <param name="action"></param>
        /// <param name="contentType"></param>
        public static void Put(string url, byte[] contentBytes, RequestGenericEvent action, string contentType = "application/octet-stream")
        {
            _mono.StartCoroutine(_Put(url, contentBytes, action, contentType));
        }
        #endregion

        #region private Method
        private static IEnumerator _DownloadText(string url, RequestTextEvent action)
        {
            using (UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET))
            {
                request.downloadHandler = new DownloadHandlerBuffer();

                if (action.downloadProgress != null)
                    _mono.StartCoroutine(DownloadProgress(request, action.downloadProgress));

                yield return request.SendWebRequest();
                Dispose(request, () => { action.action(request.downloadHandler.text); }, action.error, action.error404, action.error500);
            }
        }

        private static IEnumerator _DownloadData(string url, RequestDataEvent action)
        {
            using (UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET))
            {
                request.downloadHandler = new DownloadHandlerBuffer();

                if (action.downloadProgress != null)
                    _mono.StartCoroutine(DownloadProgress(request, action.downloadProgress));

                yield return request.SendWebRequest();
                Dispose(request, () => { action.action(request.downloadHandler.data); }, action.error, action.error404, action.error500);
            }
        }

        private static IEnumerator _DownloadFile(string url, string savePath, RequestFileEvent action)
        {
            if (!File.Exists(savePath))
            {
                using (UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET))
                {
                    request.downloadHandler = new DownloadHandlerFile(savePath);

                    if (action.downloadProgress != null)
                        _mono.StartCoroutine(DownloadProgress(request, action.downloadProgress));

                    yield return request.SendWebRequest();
                    Dispose(request, () => { action.action(); }, action.error, action.error404, action.error500);
                }
            }
            else if (action.failedCreateFile != null) action.failedCreateFile();
        }

        private static IEnumerator _DownloadSprite(string url, RequestSpriteEvent action)
        {
            using (UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET))
            {
                DownloadHandlerTexture downloadTexture = new DownloadHandlerTexture(true);
                request.downloadHandler = downloadTexture;

                if (action.downloadProgress != null)
                    _mono.StartCoroutine(DownloadProgress(request, action.downloadProgress));

                yield return request.SendWebRequest();
                Dispose(request, () =>
                {
                    Texture2D t = downloadTexture.texture;
                    action.action(Sprite.Create(t, new Rect(0, 0, t.width, t.height), Vector2.zero, 1f), downloadTexture.data);
                }, action.error, action.error404, action.error500);
            }
        }

        private static IEnumerator _DownloadAudioClip(string url, AudioType audioType, RequestAudioClipEvent action)
        {
            using (UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(url, audioType))
            {

                if (action.downloadProgress != null)
                    _mono.StartCoroutine(DownloadProgress(request, action.downloadProgress));

                yield return request.SendWebRequest();
                Dispose(request, () => { action.action(DownloadHandlerAudioClip.GetContent(request)); }, action.error, action.error404, action.error500);
            }
        }

        private static IEnumerator _DownloadMovieTexture(string url, RequestMovieTextureEvent action)
        {
            using (UnityWebRequest request = UnityWebRequestMultimedia.GetMovieTexture(url))
            {

                if (action.downloadProgress != null)
                    _mono.StartCoroutine(DownloadProgress(request, action.downloadProgress));

                yield return request.SendWebRequest();
                Dispose(request, () => { action.action(DownloadHandlerMovieTexture.GetContent(request)); }, action.error, action.error404, action.error500);
            }
        }

        private static IEnumerator _DownloadAssetBundle(string url, RequestAssetBundleEvent action)
        {
            using (UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET))
            {
                DownloadHandlerAssetBundle handler = new DownloadHandlerAssetBundle(request.url, uint.MaxValue);
                request.downloadHandler = handler;

                if (action.downloadProgress != null)
                    _mono.StartCoroutine(DownloadProgress(request, action.downloadProgress));

                yield return request.SendWebRequest();
                Dispose(request, () => { action.action(handler.assetBundle); }, action.error, action.error404, action.error500);
            }
        }

        private static IEnumerator _Get(string url, RequestGenericEvent action)
        {
            using (UnityWebRequest request = UnityWebRequest.Get(url))
            {
                yield return request.SendWebRequest();
                Dispose(request, () => { action.action(); }, action.error, action.error404, action.error500);
            }
        }

        private static IEnumerator _Post(string url, List<IMultipartFormSection> lstformData, RequestGenericEvent action)
        {
            using (UnityWebRequest request = UnityWebRequest.Post(url, lstformData))
            {
                yield return request.SendWebRequest();
                Dispose(request, () => { action.action(); }, action.error, action.error404, action.error500);
            }
        }

        private static IEnumerator _Put(string url, byte[] contentBytes, RequestGenericEvent action, string contentType)
        {
            using (UnityWebRequest request = new UnityWebRequest())
            {
                UploadHandler uploader = new UploadHandlerRaw(contentBytes);
                uploader.contentType = contentType;
                request.uploadHandler = uploader;

                if (action.progress != null)
                    _mono.StartCoroutine(UploadProgress(request, action.progress));

                yield return request.SendWebRequest();
                Dispose(request, () => { action.action(); }, action.error, action.error404, action.error500);
            }
        }

        private static bool IsNull(string url, BaseRequestEvent requestAction, object action)
        {
            if (string.IsNullOrEmpty(url) || requestAction == null || action == null)
                return true;
            return false;
        }

        private static IEnumerator DownloadProgress(UnityWebRequest request, Action<float> action)
        {
            while (!request.isDone)
            {
                yield return null;
                action(request.downloadProgress);
            }
            //request.Abort();
            Resources.UnloadUnusedAssets();
        }

        private static IEnumerator UploadProgress(UnityWebRequest request, Action<float> action)
        {
            while (!request.isDone)
            {
                yield return null;
                action(request.downloadProgress);
            }
        }

        private static void Dispose(UnityWebRequest request, Action ok, Action<string> error, Action error404, Action error500)
        {
            if (request.isHttpError || request.isNetworkError)
            {
                if (error != null) error(request.error);
            }
            else
            {
                switch (request.responseCode)
                {
                    case 200: if (ok != null) ok(); break;
                    case 404: if (error404 != null) error404(); break;
                    case 500: if (error500 != null) error500(); break;
                }
            }
        }
        #endregion
    }

    public class BaseRequestEvent
    {
        public Action<float> downloadProgress;
        public Action<string> error;
        public Action error404;
        public Action error500;
    }

    /// <summary>
    /// 文本请求事件
    /// </summary>
    public class RequestTextEvent : BaseRequestEvent
    {
        public Action<string> action;
    }

    /// <summary>
    /// 数据请求事件
    /// </summary>
    public class RequestDataEvent : BaseRequestEvent
    {
        public Action<byte[]> action;
    }

    /// <summary>
    /// 文件请求事件
    /// </summary>
    public class RequestFileEvent : BaseRequestEvent
    {
        public Action action;
        public Action failedCreateFile;
    }

    /// <summary>
    /// 图片请求事件
    /// </summary>
    public class RequestSpriteEvent : BaseRequestEvent
    {
        public Action<Sprite, byte[]> action;
    }

    /// <summary>
    /// 音频请求事件
    /// </summary>
    public class RequestAudioClipEvent : BaseRequestEvent
    {
        public Action<AudioClip> action;
    }

    /// <summary>
    /// 视频请求事件
    /// </summary>
    public class RequestMovieTextureEvent : BaseRequestEvent
    {
        public Action<MovieTexture> action;
    }

    /// <summary>
    /// AB包请求事件
    /// </summary>
    public class RequestAssetBundleEvent : BaseRequestEvent
    {
        public Action<AssetBundle> action;
    }

    /// <summary>
    /// 通用的请求事件
    /// </summary>
    public class RequestGenericEvent
    {
        public Action action;
        public Action<float> progress;
        public Action<string> error;
        public Action error404;
        public Action error500;
    }
}
